package com.boot2.core.utils;

import com.boot2.core.exception.HlSchedulerException;
import org.junit.Test;
import org.quartz.CronExpression;
import org.quartz.TriggerUtils;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.util.Date;
import java.util.List;

/**
 * @author zhangweilin
 * @Description: 时间cron表达式工具类
 * @date 2019/5/9
 */
public class CronExpressionUtil {

    /**
     * 获取非一次性执行的表达式
     *
     * @param timeStr   时间, 只接受 HH:mm:ss或 yyyy-MM-dd HH:mm:ss,
     *                  如果循环周期是每个指定秒，即每分内，则分小时日月年无效
     *                  如果循环周期是每个指定分，即每小时内，则小时日月年无效
     *                  如果循环周期是每个指定小时，即每天内，则日月年无效
     *                  如果循环周期是每个指定几号的，即每月内，则月年无效
     *                  如果循环周期是每个指定几月的，即每年内，则年无效
     * @param loopRule  重复规则，枚举为内部类
     * @param loopWeeks 重复的周，只有当loopRule=Week时才有效，格式为类似 1,3,5，即周日周二周四
     * @return
     */
    public static String getExpression(String timeStr, LoopRule loopRule, String loopWeeks) {
        Assert.isTrue(StringUtils.isNotEmpty(timeStr), "时间不能为空");
        Date time = DateUtil.getFormatDate(timeStr, DateUtil.PATTERN_HH_mm_ss);
        if (null == time) {
            time = DateUtil.getFormatDate(timeStr, DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss);
        }
        return getExpression(time, loopRule, loopWeeks);
    }

    /**
     * 获取非一次性执行的表达式
     *
     * @param loopRule
     * @param loopWeeks
     * @param time
     * @return
     */
    public static String getExpression(Date time, LoopRule loopRule, String loopWeeks) {
        String secStr = "*";
        String minStr = "*";
        String hourStr = "*";
        String dayStr = "*";
        String monthStr = "*";
        String yearStr = "*";
        if (null != time) {
            int hour = DateUtil.getHour(time);
            int min = DateUtil.getMin(time);
            int sec = DateUtil.getSec(time);

            int day = DateUtil.getDay(time);
            int month = DateUtil.getMonth(time);
            int year = DateUtil.getYear(time);

            hourStr = String.valueOf(hour);
            minStr = String.valueOf(min);
            secStr = String.valueOf(sec);

            dayStr = String.valueOf(day);
            monthStr = String.valueOf(month);
            yearStr = String.valueOf(year);

        }
        String expression = "";
        switch (loopRule) {
            case EveryMin:
                //每分内
                expression = secStr + " * * * * ? *";
                break;
            case EveryHour:
                //每小时内
                expression = secStr + " " + minStr + " * * * ? *";
                break;
            case EveryDay:
                //每天
                expression = secStr + " " + minStr + " " + hourStr + " * * ? *";
                break;
            case EveryMonth:
                //每月
                expression = secStr + " " + minStr + " " + hourStr + " "+dayStr+" * ? *";
                break;
            case EveryYear:
                //每月
                expression = secStr + " " + minStr + " " + hourStr + " "+dayStr+" "+monthStr+" ? *";
                break;

            case Week1To5:
                //周一到周五
                expression = secStr + " " + minStr + " " + hourStr + " ? * 2-6 *";
                break;
            case Week67:
                // 5:周六周日
                expression = secStr + " " + minStr + " " + hourStr + " ? * 7,1 *";
                break;
            case Week:
                // 6:按星期自定义.
                expression = secStr + " " + minStr + " " + hourStr + " ? * " + loopWeeks + " *";
                break;
            default:
                break;

        }
//        if (loopRule.equals(LoopRule.EveryDay)) {
//            //每天
//            expression = secStr + " " + minStr + " " + hourStr + " * * ? *";
//        } else if (loopRule.equals(LoopRule.Week1To5)) {
//            //周一到周五
//            expression = secStr + " " + minStr + " " + hourStr + " ? * 2-6 *";
//        } else if (loopRule.equals(LoopRule.Week67)) {
//            // 5:周六周日
//            expression = secStr + " " + minStr + " " + hourStr + " ? * 7,1 *";
//        } else if (loopRule.equals(LoopRule.Week)) {
//            // 6:按星期自定义.
//            expression = secStr + " " + minStr + " " + hourStr + " ? * " + loopWeeks + " *";
//        }
        return expression;
    }

    /**
     * 获取一次性执行的表达式
     *
     * @param dateStr 日期, 只接受 yyyy-MM-dd
     * @param timeStr 时间, 只接受 HH:mm:ss
     * @return
     */
    public static String getOneTimeExpression(String dateStr, String timeStr) {
        Assert.isTrue(StringUtils.isNotEmpty(dateStr) && StringUtils.isNotEmpty(timeStr), "日期和时间至少有一个不能为空");
        Date date = null;
        Date time = null;

        if (StringUtils.isNotEmpty(dateStr)) {
            try {
                date = DateUtil.getFormatDate(dateStr, DateUtil.PATTERN_yyyy_MM_dd);
            } catch (Exception e) {
//                e.printStackTrace();
                date = DateUtil.getFormatDate(timeStr, DateUtil.PATTERN_HH_mm_ss);
            }
        }
        if (StringUtils.isNotEmpty(timeStr)) {
            time = DateUtil.getFormatDate(timeStr, DateUtil.PATTERN_HH_mm_ss);
        }
        return getOneTimeExpression(date, time);
    }

    /**
     * 获取一次性执行的表达式
     *
     * @param date
     * @param time
     * @return
     */
    public static String getOneTimeExpression(Date date, Date time) {
        String secStr = "*";
        String minStr = "*";
        String hourStr = "*";
        String dayStr = "*";
        String monthStr = "*";
        String yearStr = "*";
        if (null != date) {
            int year = DateUtil.getYear(date);
            int month = DateUtil.getMonth(date);
            int day = DateUtil.getDay(date);
            yearStr = String.valueOf(year);
            monthStr = String.valueOf(month);
            dayStr = String.valueOf(day);
        }

        if (null != time) {
            int hour = DateUtil.getHour(time);
            int min = DateUtil.getMin(time);
            int sec = DateUtil.getSec(time);
            hourStr = String.valueOf(hour);
            minStr = String.valueOf(min);
            secStr = String.valueOf(sec);
        }
        String expression = secStr + " " + minStr + " " + hourStr + " " + dayStr + " " + monthStr + " ? " + yearStr;
        return expression;
    }

    /**
     * 校验定时任务格式
     *
     * @param cronExpression
     * @return
     * @songwen 2018年8月28日
     */
    public static boolean validateCronExpression(String cronExpression) {
        if (StringUtils.isBlank(cronExpression)) {
            return false;
        }
        return CronExpression.isValidExpression(cronExpression);
    }

    /**
     * 校验表达式是否能触发(相对于当前时间)
     *
     * @param expressionStr
     * @return
     * @throws ParseException
     * @songwen 2018年8月28日
     */
    public static boolean isCanDoExpression(String expressionStr) {
        //先判断表达式格式是否正确
        if (!validateCronExpression(expressionStr)) {
            return false;
        }
        CronTriggerImpl triggerImpl = new CronTriggerImpl();
        try {
            triggerImpl.setCronExpression(expressionStr);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new HlSchedulerException("非法的cron expression表达式.", e);
        }
        Date date = triggerImpl.computeFirstFireTime(null);
        return date != null && date.after(new Date());
    }

    /**
     * 获取一段时间内可以运行日期
     *
     * @param expressionStr
     * @param beginDate
     * @param endDate
     * @return
     * @throws ParseException
     * @songwen 2018年8月28日
     */
    public static List<Date> getCanDoBetween(String expressionStr, Date beginDate, Date endDate) throws ParseException {
        //先判断表达式格式是否正确
        if (!validateCronExpression(expressionStr)) {
            return null;
        }
        if (endDate.before(beginDate)) {
            return null;
        }
        CronTriggerImpl triggerImpl = new CronTriggerImpl();
        triggerImpl.setCronExpression(expressionStr);
        return TriggerUtils.computeFireTimesBetween(triggerImpl, null, beginDate, endDate);
    }


    @Test
    public void test() {
        String oneTimeExpression = getOneTimeExpression("2019-05-09", "18:09:36");
        System.out.println("oneTimeExpression = " + oneTimeExpression);
        oneTimeExpression = getOneTimeExpression("2019-05-01", "18:09:36");
        System.out.println("oneTimeExpression = " + oneTimeExpression);
        oneTimeExpression = getOneTimeExpression("2019-05-01", "20:29:16");
        System.out.println("oneTimeExpression = " + oneTimeExpression);


        String expression = getExpression("18:09:36", LoopRule.EveryDay, "");
        System.out.println("expression = " + expression);
        expression = getExpression("18:09:36", LoopRule.Week1To5, "");
        System.out.println("expression = " + expression);
        expression = getExpression("18:09:36", LoopRule.Week67, "");
        System.out.println("expression = " + expression);
        expression = getExpression("18:09:36", LoopRule.Week, "2,3,5");
        System.out.println("expression = " + expression);
    }


    /**
     * 重复规则
     *
     * @Description:
     * @CreateDate:2018年3月14日 下午2:16:12
     * @Author:zhangweilin
     */
    public enum LoopRule {
        /**
         * 重复规则
         */
//        OneTime(0, "只执行一次"),
        EveryMin(-1, "每分"),
        EveryHour(0, "每小时"),
        EveryDay(1, "每天"),
        EveryMonth(2, "每月"),
        EveryYear(3, "每年"),
        //        Workday(2, "法定工作日"),
//        Holiday(3, "法定休息日"),
        Week1To5(4, "周一到周五"),
        Week67(5, "周六周日"),
        Week(6, "按星期自定义");

        private int index;
        private String name;

        LoopRule(int index, String name) {
            this.index = index;
            this.name = name;
        }

        public static LoopRule get(int index) {
            for (LoopRule c : LoopRule.values()) {
                if (c.getIndex() == index) {
                    return c;
                }
            }
            return null;
        }

        public String getName() {
            return name;
        }

        public int getIndex() {
            return index;
        }
    }


    /**
     * 返回一个布尔值代表一个给定的Cron表达式的有效性
     *
     * @param cronExpression Cron表达式
     * @return boolean 表达式是否有效
     */
    public static boolean isValid(String cronExpression) {
        return CronExpression.isValidExpression(cronExpression);
    }

    /**
     * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
     *
     * @param cronExpression Cron表达式
     * @return String 无效时返回表达式错误描述,如果有效返回null
     */
    public static String getInvalidMessage(String cronExpression) {
        try {
            new CronExpression(cronExpression);
            return null;
        } catch (ParseException pe) {
            return pe.getMessage();
        }
    }

    /**
     * 返回下一个执行时间根据给定的Cron表达式
     *
     * @param cronExpression Cron表达式
     * @return Date 下次Cron表达式执行时间
     */
    public static Date getNextExecution(String cronExpression) {
        try {
            CronExpression cron = new CronExpression(cronExpression);
            return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
        } catch (ParseException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }
}
